#!/usr/bin/env python
#
# Routines for reading simulator job trace files produced by the
# job_format_writer input module.
#
# Author:   Mike Murphy <mamurph@cs.clemson.edu>
# Revision: 23 September 2009
#
#   Copyright 2009 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

'''
Mechanism for reading job trace files produced by the job_format_writer
input module. Jobs are submitted to the grid automatically.
'''

from common_reader import CommonReader
from job_model import Job
from results import DiscardResult

JOB_FORMAT = 0.90


class JobReader(CommonReader):
   '''
   Mechanism for reading job trace files, creating Job objects from the
   traces, and submitting the resulting jobs to the grid. An optional job
   modification function may be used, which will be called just before
   submitting the job to the grid. This function must take two arguments:
   the site name to which the job is to be submitted, and the Job object
   itself. The function must return a string containing the site name to
   which the job is to be submitted, or None if the job is to be discarded.
   '''
   def __init__(self, filename, fh, simobj, grid, modfn=None):
      '''
      @param filename: Name of open job file
      @type filename: str
      @param fh: Handle to open job file (r mode)
      @param simobj: Reference to the simulator instance
      @param grid: Grid object to which jobs are submitted
      @param modfn: Optional function to be called just before job submission
      '''
      prefix = 'jobmeta_' + filename + '_'
      CommonReader.__init__(self, filename, fh, simobj, JOB_FORMAT, prefix)
      self.add_handler(':', self.h_colon)
      self.grid = grid
      self.modfn = modfn
   def submit_job(self, site_name, job):
      '''
      Submits a job to the grid at the current simulated time

      @param site_name: Name of site to which job is to be submitted
      @type site_name: str
      @param job: Job to submit
      '''
      self.grid.add_submit_event(self.time, job, site_name)
   def h_colon(self):
      '''
      Parse handler for job definitions.

      @return: True
      '''
      if not self.in_clock_block:
         self.error('Job definitions are valid only in clock blocks')
      if len(self.parts) not in (6,7):
         self.error('Job definition requires exactly 5 or 6 arguments')
      try:
         parts = self.parts
         job_id = parts[1]
         site_name = parts[2]
         lf = float(parts[3])
         li = int(lf)
         length = lf
         if li == lf:
            length = li
         cpu = float(parts[4])
         vo = parts[5]
         queue = -1
         if len(parts) == 7:
            qf = float(parts[6])
            qi = int(qf)
            queue = qf
            if qi == qf:
               queue = qi
         #
         j = Job(length, cpu=cpu, name=job_id, vo=vo, queue_time=queue)
         if length < 0:
            j.length = 0
            j.error = True
         if site_name is None:
            site_name = ''
         if self.modfn is not None:
            site_name = self.modfn(site_name, j)
         if site_name is not None:
            self.submit_job(site_name, j)
         else:
            d = DiscardResult(self.time, \
                              'Job rejected by modification function', j)
            self.sim.add_result(d)
         #
      except Exception, msg:
         self.error(msg)
      return True
#
